﻿using FluentNHibernate.Automapping;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Mapping;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using System;
using System.Linq;
using Utility.Global.Exceptions;

namespace Utility.Persistence_Service.Implementations
{
    public class UnitOfWorkFactoryImpl : UnitOfWorkFactory
    {
        private ISessionFactory sessionFactory;

        private UnitOfWorkFactoryImpl(ISessionFactory sessionFactory)
        {
            this.sessionFactory = sessionFactory;
        }

        public static UnitOfWorkFactory CreateUnitOfWork(bool generateSchema)
        {
            string connectionString = GetDatabaseConnectionString();
            Configuration configuration = new Configuration();
            FluentConfiguration fluentConfiguration = Fluently.Configure(configuration)
                .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString));
            fluentConfiguration.Mappings(m => FindFluentMappings(m));
            configuration = fluentConfiguration.ExposeConfiguration(config => {
                if (generateSchema)
                {
                    new SchemaExport(config).Execute(true, true, false);
                }
                else
                {
                    new SchemaUpdate(config).Execute(false, true);
                }
            }).BuildConfiguration();

            ISessionFactory sessionFactory = configuration.BuildSessionFactory();
            UnitOfWorkFactory unitOfWorkFactory = new UnitOfWorkFactoryImpl(sessionFactory);
            return unitOfWorkFactory;
        }

        private static string GetDatabaseConnectionString()
        {
            System.Configuration.ConnectionStringSettings connectionSettings = System.Configuration.ConfigurationManager.ConnectionStrings["Database"];
            if (connectionSettings == null)
            {
                throw new TechnicalProblemException("Die Datenbank muss in der App.config konfiguriert sein.");
            }
            return connectionSettings.ConnectionString;
        }

        public static UnitOfWorkFactory CreateUnitOfWork()
        {
            return CreateUnitOfWork(false);
        }

        public UnitOfWork NewUnitOfWork()
        {
            return new UnitOfWorkImpl(sessionFactory);
        }

        private static void FindFluentMappings(MappingConfiguration mappingConfiguration)
        {
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                // Hat die Assembly Fluent-Mappings (Typ: ClassMap<>)?
                if ((assembly.GetTypes().Where(
                    type => type != typeof(AutoMapping<>) && 
                        type.BaseType != null && 
                        type.BaseType.IsGenericType && 
                        type.BaseType.GetGenericTypeDefinition() == typeof(ClassMap<>)).ToList()).Count > 0)
                {
                    mappingConfiguration.FluentMappings.AddFromAssembly(assembly);
                }
            }
        }
    }
}

